/*
 * Copyright 2001 Alexander Boverman and the original authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtHtmlEntities.h"
#include "WtContext.h"
#include <tcl.h>

WtHtmlMaps wtHtmlMaps = {0, NULL, 10000, NULL, NULL};

int wtHtmlEntityChars[] = {
  34,
  38,
  39,
  60,
  62,
  160,
  161,
  162,
  163,
  164,
  165,
  166,
  167,
  168,
  169,
  170,
  171,
  172,
  173,
  174,
  175,
  176,
  177,
  178,
  179,
  180,
  181,
  182,
  183,
  184,
  185,
  186,
  187,
  188,
  189,
  190,
  191,
  192,
  193,
  194,
  195,
  196,
  197,
  198,
  199,
  200,
  201,
  202,
  203,
  204,
  205,
  206,
  207,
  208,
  209,
  210,
  211,
  212,
  213,
  214,
  215,
  216,
  217,
  218,
  219,
  220,
  221,
  222,
  223,
  224,
  225,
  226,
  227,
  228,
  229,
  230,
  231,
  232,
  233,
  234,
  235,
  236,
  237,
  238,
  239,
  240,
  241,
  242,
  243,
  244,
  245,
  246,
  247,
  248,
  249,
  250,
  251,
  252,
  253,
  254,
  255,
  338,
  339,
  352,
  353,
  376,
  402,
  710,
  732,
  913,
  914,
  915,
  916,
  917,
  918,
  919,
  920,
  921,
  922,
  923,
  924,
  925,
  926,
  927,
  928,
  929,
  931,
  932,
  933,
  934,
  935,
  936,
  937,
  945,
  946,
  947,
  948,
  949,
  950,
  951,
  952,
  953,
  954,
  955,
  956,
  957,
  958,
  959,
  960,
  961,
  962,
  963,
  964,
  965,
  966,
  967,
  968,
  969,
  977,
  978,
  982,
  8194,
  8195,
  8201,
  8204,
  8205,
  8206,
  8207,
  8211,
  8212,
  8216,
  8217,
  8218,
  8220,
  8221,
  8222,
  8224,
  8225,
  8226,
  8230,
  8240,
  8242,
  8243,
  8249,
  8250,
  8254,
  8260,
  8364,
  8465,
  8472,
  8476,
  8482,
  8501,
  8592,
  8593,
  8594,
  8595,
  8596,
  8629,
  8656,
  8657,
  8658,
  8659,
  8660,
  8704,
  8706,
  8707,
  8709,
  8711,
  8712,
  8713,
  8715,
  8719,
  8721,
  8722,
  8727,
  8730,
  8733,
  8734,
  8736,
  8743,
  8744,
  8745,
  8746,
  8747,
  8756,
  8764,
  8773,
  8776,
  8800,
  8801,
  8804,
  8805,
  8834,
  8835,
  8836,
  8838,
  8839,
  8853,
  8855,
  8869,
  8901,
  8968,
  8969,
  8970,
  8971,
  9001,
  9002,
  9674,
  9824,
  9827,
  9829,
  9830
};

const char *wtHtmlEntityStrings[] = {
  "quot",
  "amp",
  "apos",
  "lt",
  "gt",
  "nbsp",
  "iexcl",
  "cent",
  "pound",
  "curren",
  "yen",
  "brvbar",
  "sect",
  "uml",
  "copy",
  "ordf",
  "laquo",
  "not",
  "shy",
  "reg",
  "macr",
  "deg",
  "plusmn",
  "sup2",
  "sup3",
  "acute",
  "micro",
  "para",
  "middot",
  "cedil",
  "sup1",
  "ordm",
  "raquo",
  "frac14",
  "frac12",
  "frac34",
  "iquest",
  "Agrave",
  "Aacute",
  "Acirc",
  "Atilde",
  "Auml",
  "Aring",
  "AElig",
  "Ccedil",
  "Egrave",
  "Eacute",
  "Ecirc",
  "Euml",
  "Igrave",
  "Iacute",
  "Icirc",
  "Iuml",
  "ETH",
  "Ntilde",
  "Ograve",
  "Oacute",
  "Ocirc",
  "Otilde",
  "Ouml",
  "times",
  "Oslash",
  "Ugrave",
  "Uacute",
  "Ucirc",
  "Uuml",
  "Yacute",
  "THORN",
  "szlig",
  "agrave",
  "aacute",
  "acirc",
  "atilde",
  "auml",
  "aring",
  "aelig",
  "ccedil",
  "egrave",
  "eacute",
  "ecirc",
  "euml",
  "igrave",
  "iacute",
  "icirc",
  "iuml",
  "eth",
  "ntilde",
  "ograve",
  "oacute",
  "ocirc",
  "otilde",
  "ouml",
  "divide",
  "oslash",
  "ugrave",
  "uacute",
  "ucirc",
  "uuml",
  "yacute",
  "thorn",
  "yuml",
  "OElig",
  "oelig",
  "Scaron",
  "scaron",
  "Yuml",
  "fnof",
  "circ",
  "tilde",
  "Alpha",
  "Beta",
  "Gamma",
  "Delta",
  "Epsilon",
  "Zeta",
  "Eta",
  "Theta",
  "Iota",
  "Kappa",
  "Lambda",
  "Mu",
  "Nu",
  "Xi",
  "Omicron",
  "Pi",
  "Rho",
  "Sigma",
  "Tau",
  "Upsilon",
  "Phi",
  "Chi",
  "Psi",
  "Omega",
  "alpha",
  "beta",
  "gamma",
  "delta",
  "epsilon",
  "zeta",
  "eta",
  "theta",
  "iota",
  "kappa",
  "lambda",
  "mu",
  "nu",
  "xi",
  "omicron",
  "pi",
  "rho",
  "sigmaf",
  "sigma",
  "tau",
  "upsilon",
  "phi",
  "chi",
  "psi",
  "omega",
  "thetasym",
  "upsih",
  "piv",
  "ensp",
  "emsp",
  "thinsp",
  "zwnj",
  "zwj",
  "lrm",
  "rlm",
  "ndash",
  "mdash",
  "lsquo",
  "rsquo",
  "sbquo",
  "ldquo",
  "rdquo",
  "bdquo",
  "dagger",
  "Dagger",
  "bull",
  "hellip",
  "permil",
  "prime",
  "Prime",
  "lsaquo",
  "rsaquo",
  "oline",
  "frasl",
  "euro",
  "image",
  "weierp",
  "real",
  "trade",
  "alefsym",
  "larr",
  "uarr",
  "rarr",
  "darr",
  "harr",
  "crarr",
  "lArr",
  "uArr",
  "rArr",
  "dArr",
  "hArr",
  "forall",
  "part",
  "exist",
  "empty",
  "nabla",
  "isin",
  "notin",
  "ni",
  "prod",
  "sum",
  "minus",
  "lowast",
  "radic",
  "prop",
  "infin",
  "ang",
  "and",
  "or",
  "cap",
  "cup",
  "int",
  "there4",
  "sim",
  "cong",
  "asymp",
  "ne",
  "equiv",
  "le",
  "ge",
  "sub",
  "sup",
  "nsub",
  "sube",
  "supe",
  "oplus",
  "otimes",
  "perp",
  "sdot",
  "lceil",
  "rceil",
  "lfloor",
  "rfloor",
  "lang",
  "rang",
  "loz",
  "spades",
  "clubs",
  "hearts",
  "diams"
};

void WtInitHtmlEntities()
{
  if (!wtHtmlMaps.initialized) {
    Tcl_MutexLock(&wtHtmlMaps.htmlLock);
    if (!wtHtmlMaps.initialized) {
      WtDoInitHtmlEntities();
      wtHtmlMaps.initialized = 1;
    }
    Tcl_MutexUnlock(&wtHtmlMaps.htmlLock);
  }
}

void WtDoInitHtmlEntities()
{
  int i, created;
  Tcl_HashEntry *entry;
  char str[256];

  /* Chars to entities */

  wtHtmlMaps.charsToEnts = (char **)ckalloc(sizeof(char *) *
    wtHtmlMaps.maxHtmlChar);
  memset(wtHtmlMaps.charsToEnts, '\0',
    sizeof(char *) * wtHtmlMaps.maxHtmlChar);

  for (i = 0; i < (sizeof(wtHtmlEntityChars) / sizeof(int)); i++) {
    wtHtmlMaps.charsToEnts[wtHtmlEntityChars[i]] =
      strdup(wtHtmlEntityStrings[i]);
  }

  for (i = 0; i < 256; i++) {
    if (!wtHtmlMaps.charsToEnts[i]) {
      sprintf(str, "&#%d;", i);
      wtHtmlMaps.charsToEnts[i] = strdup(str);
    }
  }

  /* Entities to chars */

  wtHtmlMaps.entsToChars =
    (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
  Tcl_InitHashTable(wtHtmlMaps.entsToChars, TCL_STRING_KEYS);

  for (i = 0; i < (sizeof(wtHtmlEntityStrings) / sizeof(char *)); i++) {
    entry = Tcl_CreateHashEntry(wtHtmlMaps.entsToChars,
      wtHtmlEntityStrings[i], &created);
    Tcl_SetHashValue(entry, wtHtmlEntityChars[i]);
  }
}

void WtDeleteHtmlEntities()
{
  if (wtHtmlMaps.initialized) {
    Tcl_MutexLock(&wtHtmlMaps.htmlLock);
    if (wtHtmlMaps.initialized) {
      WtDoDeleteHtmlEntities();
      wtHtmlMaps.initialized = 0;
    }
    Tcl_MutexUnlock(&wtHtmlMaps.htmlLock);
  }
}

void WtDoDeleteHtmlEntities()
{
  int i;
  for (i = 0; i < wtHtmlMaps.maxHtmlChar; i++) {
    if (wtHtmlMaps.charsToEnts[i]) {
      free(wtHtmlMaps.charsToEnts[i]);
    }
  }
  ckfree((char *)wtHtmlMaps.charsToEnts);

  Tcl_DeleteHashTable(wtHtmlMaps.entsToChars);
  ckfree((char *)wtHtmlMaps.entsToChars);
  wtHtmlMaps.entsToChars = NULL;
}

/* Code bits for HTML encoding taken from PHP source */

Tcl_Obj *WtEncodeHtml(const char *utfStr, int options)
{
  Tcl_UniChar uniCh;
  const char *p, *q, *ent;
  char buf[256];
  Tcl_Obj *dest;

  WtInitHtmlEntities();

  p = utfStr;
  dest = Tcl_NewStringObj(NULL, 0);

  for (;;) {
    Tcl_UtfToUniChar(p, &uniCh);
    if (uniCh == 0) {
      break;
    }

    q = Tcl_UtfNext(p);

    if (uniCh == '<' || uniCh == '>' || uniCh == '&') {
      if (options & WT_ENT_SPECIAL_CHARS || options & WT_ENT_DEFAULT ||
          options & WT_ENT_ALL) {
        Tcl_AppendStringsToObj(dest, "&",
          wtHtmlMaps.charsToEnts[uniCh], ";", NULL);
      } else {
        Tcl_AppendToObj(dest, p, q - p);
      }
    } else if (uniCh == '"') {
      if (options & WT_ENT_DOUBLE_QUOTE || options & WT_ENT_DEFAULT ||
          options & WT_ENT_ALL) {
        Tcl_AppendStringsToObj(dest, "&", wtHtmlMaps.charsToEnts[uniCh],
          ";", NULL);
      } else {
        Tcl_AppendToObj(dest, p, q - p);
      }
    } else if (uniCh == '\'') {
      if (options & WT_ENT_SINGLE_QUOTE || options & WT_ENT_DEFAULT ||
          options & WT_ENT_ALL) {
        Tcl_AppendStringsToObj(dest, "&", wtHtmlMaps.charsToEnts[uniCh],
          ";", NULL);
      } else {
        Tcl_AppendToObj(dest, p, q - p);
      }
    } else if (uniCh < wtHtmlMaps.maxHtmlChar &&
        (ent = wtHtmlMaps.charsToEnts[uniCh])) {
      if (options & WT_ENT_NAMED_ENTITIES || options & WT_ENT_ALL) {
        Tcl_AppendStringsToObj(dest, "&",
          wtHtmlMaps.charsToEnts[uniCh], ";", NULL);
      } else {
        Tcl_AppendToObj(dest, p, q - p);
      }
    } else if (options & WT_ENT_UNKNOWN_CHARS || options & WT_ENT_ALL) {
      sprintf(buf, "&#x%X;", uniCh);
      Tcl_AppendToObj(dest, buf, -1);
    } else {
      Tcl_AppendToObj(dest, p, q - p);
    }

    p = q;
  }

  return dest;
}
